<!doctype html><html lang=zh-hans>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta http-equiv=x-ua-compatible content="IE=edge">
<meta name=author content="小李刀刀">
<meta name=description content="Spiral 框架包含一系列的接口和组件，旨在简化应用程序中的依赖注入管理和对象构造。容器的实现与 PSR-11 容器规范完全兼容。">
<meta name=theme-color content="#3f51b5">
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/academicons/1.8.6/css/academicons.min.css integrity="sha256-uFVgMKfistnJAfoCUQigIl+JfUaP47GrRKjf6CTPVmw=" crossorigin=anonymous>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-1/css/all.min.css integrity="sha256-4w9DunooKSr3MFXHXWyFER38WmPdm361bQS/2KUWZbU=" crossorigin=anonymous>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css integrity="sha256-Vzbj7sDDS/woiFS3uNKo8eIuni59rjyNGtXfstRzStA=" crossorigin=anonymous>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/tomorrow-night.min.css crossorigin=anonymous title=hl-light>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/tomorrow-night.min.css crossorigin=anonymous title=hl-dark disabled>
<script src=https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js integrity="sha256-Md1qLToewPeKjfAHU1zyPwOutccPAm5tahnaw7Osw0A=" crossorigin=anonymous async></script>
<link rel=stylesheet href="https://fonts.font.im/css?family=Source+Code+Pro:300,400,500,600,700&subset=latin-ext&display=swap">
<link rel=stylesheet href=/css/academic.css>
<script>(function(b,d,e,a,g){b[a]=b[a]||[],b[a].push({'gtm.start':(new Date).getTime(),event:'gtm.js'});var f=d.getElementsByTagName(e)[0],c=d.createElement(e),h=a!='dataLayer'?'&l='+a:'';c.async=!0,c.src='https://www.googletagmanager.com/gtm.js?id='+g+h,f.parentNode.insertBefore(c,f)})(window,document,'script','dataLayer','GTM-5KZH8N7')</script>
<link rel=manifest href=/index.webmanifest>
<link rel=icon type=image/png href=/images/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_32x32_fill_lanczos_center_3.png>
<link rel=apple-touch-icon type=image/png href=/images/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_192x192_fill_lanczos_center_3.png>
<link rel=canonical href=https://studyspiral.cn/docs/framework/container/>
<meta property="twitter:card" content="summary">
<meta property="og:site_name" content="Spiral中文网">
<meta property="og:url" content="https://studyspiral.cn/docs/framework/container/">
<meta property="og:title" content="框架 - 容器和工厂 | Spiral中文网">
<meta property="og:description" content="Spiral 框架包含一系列的接口和组件，旨在简化应用程序中的依赖注入管理和对象构造。容器的实现与 PSR-11 容器规范完全兼容。"><meta property="og:image" content="https://studyspiral.cn/images/logo.svg">
<meta property="twitter:image" content="https://studyspiral.cn/images/logo.svg"><meta property="og:locale" content="zh-Hans">
<meta property="article:published_time" content="2020-02-28T16:45:40+08:00">
<meta property="article:modified_time" content="2020-04-13T00:00:42+08:00">
<script src=https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.js integrity="sha256-5VhCqFam2Cn+yjw61zbBNrbHVJ6SRydPeKopYlngbiQ=" crossorigin=anonymous></script>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.css integrity="sha256-zQ0LblD/Af8vOppw18+2anxsuaz3pWYyVWi+bTvTH8Q=" crossorigin=anonymous>
<script>window.addEventListener("load",function(){window.cookieconsent.initialise({palette:{popup:{background:"#3f51b5",text:"#fff"},button:{background:"#fff",text:"#3f51b5"}},theme:"classic",content:{message:"本网站使用cookies来确保您在本网站上获得最佳体验。",dismiss:"知道了!",link:"了解更多",href:"https://www.cookiesandyou.com"}})})</script>
<title>框架 - 容器和工厂 | Spiral中文网</title>
</head>
<body id=top data-spy=scroll data-offset=70 data-target=#TableOfContents>
<aside class=search-results id=search>
<div class=container>
<section class=search-header>
<div class="row no-gutters justify-content-between mb-3">
<div class=col-6>
<h1>搜索</h1>
</div>
<div class="col-6 col-search-close">
<a class=js-search href=#><i class="fas fa-times-circle text-muted" aria-hidden=true></i></a>
</div>
</div>
<div id=search-box>
<input name=q id=search-query placeholder=搜索... autocapitalize=off autocomplete=off autocorrect=off spellcheck=false type=search>
</div>
</section>
<section class=section-search-results>
<div id=search-hits>
</div>
</section>
</div>
</aside>
<nav class="navbar navbar-expand-lg navbar-light compensate-for-scrollbar" id=navbar-main>
<div class=container>
<div class="d-none d-lg-inline-flex">
<a class=navbar-brand href=/><img src=/images/logo.svg alt=Spiral中文网>Spiral中文网</a>
</div>
<button type=button class=navbar-toggler data-toggle=collapse data-target=#navbar-content aria-controls=navbar aria-expanded=false aria-label=切换导航>
<span><i class="fas fa-bars"></i></span>
</button>
<div class="navbar-brand-mobile-wrapper d-inline-flex d-lg-none">
<a class=navbar-brand href=/><img src=/images/logo.svg alt=Spiral中文网>Spiral中文网</a>
</div>
<div class="navbar-collapse main-menu-item collapse justify-content-end" id=navbar-content>
<ul class="navbar-nav d-md-inline-flex">
<li class=nav-item>
<a class=nav-link href=/docs/basics/quick-start/><span>教程</span></a>
</li>
<li class=nav-item>
<a class="nav-link active" href=/docs/><span>文档</span></a>
</li>
<li class=nav-item>
<a class=nav-link href=/post/><span>文章</span></a>
</li>
</ul>
</div>
<ul class="nav-icons navbar-nav flex-row ml-auto d-flex pl-md-2">
<li class=nav-item>
<a class="nav-link js-search" href=#><i class="fas fa-search" aria-hidden=true></i></a>
</li>
</ul>
</div>
</nav>
<div class="container-fluid docs">
<div class="row flex-xl-nowrap">
<div class="col-12 col-md-3 col-xl-2 docs-sidebar">
<form class="docs-search d-flex align-items-center">
<button class="btn docs-toggle d-md-none p-0 mr-3" type=button data-toggle=collapse data-target=#docs-nav aria-controls=docs-nav aria-expanded=false aria-label="Toggle section navigation">
<span><i class="fas fa-bars"></i></span>
</button>
<input name=q type=search class=form-control placeholder=搜索... autocomplete=off>
</form>
<nav class="collapse docs-links" id=docs-nav>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/extension/dotenv/>Dotenv</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/>总览</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse show">
<li>
<a href=/docs/>Spiral介绍</a>
</li>
<li>
<a href=/docs/about/contributing/>贡献指引</a>
</li>
<li>
<a href=/docs/about/semver/>版本说明</a>
</li>
<li>
<a href=/docs/about/license/>授权协议</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/http/psr-15/>定制 PSR-15 处理器</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/start/install/>快速开始</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/start/install/>安装指引</a>
</li>
<li>
<a href=/docs/start/workers/>应用工作进程</a>
</li>
<li>
<a href=/docs/start/structure/>应用程序结构</a>
</li>
<li>
<a href=/docs/start/configuration/>默认配置</a>
</li>
<li>
<a href=/docs/start/commands/>控制台命令</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/basics/quick-start/>入门基础</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/basics/quick-start/>上手指南</a>
</li>
<li>
<a href=/docs/basics/scaffolding/>脚手架</a>
</li>
<li>
<a href=/docs/basics/prototype/>原型开发辅助</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/framework/design/>核心框架</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse show">
<li>
<a href=/docs/framework/design/>设计理念</a>
</li>
<li>
<a href=/docs/framework/application-server/>应用服务器</a>
</li>
<li>
<a href=/docs/framework/config/>配置对象</a>
</li>
<li>
<a href=/docs/framework/kernel/>内核与环境</a>
</li>
<li class=active>
<a href=/docs/framework/container/>容器</a>
</li>
<li>
<a href=/docs/framework/bootloaders/>引导程序</a>
</li>
<li>
<a href=/docs/framework/scopes/>IoC 作用域</a>
</li>
<li>
<a href=/docs/framework/memory/>静态高速缓存</a>
</li>
<li>
<a href=/docs/framework/finalizers/>终结器</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/cookbook/annotated-routes/>速查手册</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/cookbook/annotated-routes/>注解式路由</a>
</li>
<li>
<a href=/docs/cookbook/injector/>容器注入器</a>
</li>
<li>
<a href=/docs/cookbook/domain-core/>领域内核、控制器</a>
</li>
<li>
<a href=/docs/cookbook/golang-library/>Golang服务集成</a>
</li>
<li>
<a href=/docs/cookbook/custom-dispatcher/>自定义调度器</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/component/files/>常用组件</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/component/files/>文件和目录</a>
</li>
<li>
<a href=/docs/component/reactor/>代码生成</a>
</li>
<li>
<a href=/docs/component/tokenizer/>静态分析工具</a>
</li>
<li>
<a href=/docs/component/metrics/>应用指标</a>
</li>
<li>
<a href=/docs/component/data-grid/>数据网格</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/console/configuration/>控制台</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/console/configuration/>安装和配置</a>
</li>
<li>
<a href=/docs/console/commands/>用户命令</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/http/configuration/>HTTP</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/http/configuration/>安装配置</a>
</li>
<li>
<a href=/docs/http/lifecycle/>请求生命周期</a>
</li>
<li>
<a href=/docs/http/request-response/>请求和响应</a>
</li>
<li>
<a href=/docs/http/routing/>路由</a>
</li>
<li>
<a href=/docs/http/errors/>错误页面</a>
</li>
<li>
<a href=/docs/http/middleware/>中间件</a>
</li>
<li>
<a href=/docs/http/golang/>Golang 中间件</a>
</li>
<li>
<a href=/docs/http/cookies/>Cookies</a>
</li>
<li>
<a href=/docs/http/session/>Session</a>
</li>
<li>
<a href=/docs/http/csrf/>CSRF 防护</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/security/encrypter/>安全</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/security/encrypter/>数据加密</a>
</li>
<li>
<a href=/docs/security/validation/>数据验证</a>
</li>
<li>
<a href=/docs/security/rbac/>基于角色的权限控制</a>
</li>
<li>
<a href=/docs/security/authentication/>用户认证</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/filters/configuration/>请求过滤</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/filters/configuration/>安装和配置</a>
</li>
<li>
<a href=/docs/filters/filter/>过滤器</a>
</li>
<li>
<a href=/docs/filters/composite/>复合过滤器</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/database/configuration/>数据库</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/database/configuration/>安装与配置</a>
</li>
<li>
<a href=/docs/database/access/>访问数据</a>
</li>
<li>
<a href=/docs/database/isolation/>逻辑隔离</a>
</li>
<li>
<a href=/docs/database/query-builders/>查询构造器</a>
</li>
<li>
<a href=/docs/database/transactions/>Transactions</a>
</li>
<li>
<a href=/docs/database/introspection/>Schema Introspection</a>
</li>
<li>
<a href=/docs/database/declaration/>Declaration</a>
</li>
<li>
<a href=/docs/database/migrations/>Migrations</a>
</li>
<li>
<a href=/docs/database/errata/>Errata</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/cycle/configuration/>Cycle ORM</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/cycle/configuration/>Configuration</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/queue/configuration/>队列任务</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/queue/configuration/>Configuration</a>
</li>
<li>
<a href=/docs/queue/scraper/>网站爬虫</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/views/configuration/>视图</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/views/configuration/>Configuration</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/stempler/configuration/>Stempler 模板</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/stempler/configuration/>Configuration</a>
</li>
<li>
<a href=/docs/stempler/directives/>Directives</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>国际化</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>GRPC</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>事件广播</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>调试及性能</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>扩展</a>
</div>
</nav>
</div>
<div class="d-none d-xl-block col-xl-2 docs-toc">
<ul class="nav toc-top">
<li><a href=# id=back_to_top class=docs-toc-title>在本页</a></li>
</ul>
<nav id=TableOfContents>
<ul>
<li><a href=#psr-11-容器>PSR-11 容器</a></li>
<li><a href=#依赖注入>依赖注入</a></li>
<li><a href=#容器配置>容器配置</a></li>
<li><a href=#单例延迟加载>单例延迟加载</a></li>
<li><a href=#工厂接口>工厂接口</a></li>
<li><a href=#解析接口>解析接口</a></li>
<li><a href=#自动装配>自动装配</a>
<ul>
<li><a href=#依赖项自动解析>依赖项自动解析</a></li>
<li><a href=#上下文自动装配>上下文自动装配</a></li>
</ul>
</li>
<li><a href=#使用单例模式>使用单例模式</a>
<ul>
<li><a href=#定义单例>定义单例</a></li>
<li><a href=#使用限制>使用限制</a></li>
<li><a href=#预热>预热</a></li>
</ul>
</li>
</ul>
</nav>
</div>
<main class="col-12 col-md-9 col-xl-8 py-md-3 pl-md-5 docs-content" role=main>
<article class=article>
<div class="alert alert-warning" role=alert>
官方文档中文版翻译工作仍在进行中，欢迎 <a href=/post/join-translation/>参与翻译</a>。
</div>
<div class=docs-article-container>
<h1>框架 - 容器和工厂</h1>
<div class=article-style>
<p>Spiral 框架包含一系列的接口和组件，旨在简化应用程序中的依赖注入管理和对象构造。</p>
<blockquote>
<p>容器的实现与
<a href=https://github.com/php-fig/container target=_blank rel=noopener>PSR-11 容器规范</a>完全兼容。</p>
</blockquote>
<h2 id=psr-11-容器>PSR-11 容器</h2>
<p>在程序代码的任意地方，你随时可以通过 <code>Psr\Container\ContainerInterface</code> 的依赖来访问容器，比如在控制器中：</p>
<pre><code class=language-php>use Psr\Container\ContainerInterface;

class HomeContoller
{
    public function index(ContainerInterface $container)
    {
        dump($container-&gt;get(App\App::class));
    }
}
</code></pre>
<h2 id=依赖注入>依赖注入</h2>
<p>Spiral 支持在类构造函数以及其它方法实现自动注入依赖项：</p>
<pre><code class=language-php>class UserMailer
{
    protected $mailer = null;

    public function __construct(Mailer $mailer)
    {
        $this-&gt;mailer = $mailer;
    }

    public function do()
    {
        $this-&gt;mailer-&gt;sendMail(...);
    }
}
</code></pre>
<p>在上面的代码中，<code>UserMailer</code> 类的依赖对象 <code>Mailer</code> 会被容器自动传递给构造函数（自动装配）。</p>
<blockquote>
<p>提示，控制器类、命令类和任务类都支持方法注入。</p>
</blockquote>
<h2 id=容器配置>容器配置</h2>
<p>你可以通过在别名或接口与其具体实现之间创建绑定关系，来配置容器。</p>
<p>在 Spiral 中，通过
<a href=/docs/framework/bootloaders/>引导程序（Bootloader）</a> 来定义上述绑定关系。</p>
<p>要配置应用容器，可以使用 <code>Spiral\Core\BinderInterface</code> 或者 <code>Spiral\Core\Container</code> 的任意一种（后者是前者的具体实现）。</p>
<p>例如，要把一个接口与其具体实现进行绑定，代码如下：</p>
<pre><code class=language-php>public function boot(Container $container)
{
    $container-&gt;bind(MyInterface::class, MyImplementation::class);
}
</code></pre>
<p>单例模式的绑定：</p>
<pre><code class=language-php>public function boot(Container $container)
{
    $container-&gt;bindSingleton(MyImplementation::class, MyImplementation::class);
}
</code></pre>
<p>如果要绑定的对象需要指定参数，方法如下：</p>
<pre><code class=language-php>public function boot(Container $container)
{
    $container-&gt;bindSingleton(MyImplementation::class, bind(MyImplementation::class, [
        'param' =&gt; 'value'
    ]));
}
</code></pre>
<blockquote>
<p>要了解如何管理依赖项的配置，请参考
<a href=/docs/framework/config/>配置对象（Config Objects）</a>。</p>
</blockquote>
<p>当然，也可以通过<code>闭包函数（closure）</code>来自动配置你的实现类：</p>
<pre><code class=language-php>public function boot(Container $container)
{
    $container-&gt;bindSingleton(MyImplementation::class, function() {
        return new MyImplementation('some-value');
    });
}
</code></pre>
<p>闭包函数本身也支持自动依赖注入，如以下示例，<code>$class</code> 参数会在 <code>MyImplementation</code> 被依赖时，由容器自动注入。</p>
<pre><code class=language-php>public function boot(Container $container)
{
    $container-&gt;bindSingleton(MyImplementation::class, function(SomeClass $class) {
        return new MyImplementation($class);
    });
}
</code></pre>
<p>可以通过容器对象的 <code>has</code> 方法检查是否包含某个类/接口的绑定：</p>
<pre><code class=language-php>dump($container-&gt;has(MyImplementation::class));
</code></pre>
<p>要从容器中移除已有的绑定，可以通过 <code>removeBinding</code> 方法：</p>
<pre><code class=language-php>$container-&gt;removeBinding(MyService::class);
</code></pre>
<h2 id=单例延迟加载>单例延迟加载</h2>
<p>如果你的类希望通过单例模式使用，可以实现 <code>Spiral\Core\Container\SingletonInterface</code> 接口，这样就无需进行单例绑定：</p>
<pre><code class=language-php>use Spiral\Core\Container\SingletonInterface;

class MyService implements SingletonInterface
{
    public function method()
    {
        //...
    }
}
</code></pre>
<p>实现了上述的接口之后，容器会自动以单例模式处理这个类：</p>
<pre><code class=language-php>protected function index(MyService $service)
{
    dump($this-&gt;container-&gt;get(MyService::class) === $service);
}
</code></pre>
<h2 id=工厂接口>工厂接口</h2>
<p>某些场景下，你可能希望构造所需的类，但不要自动解析它的构造函数所的依赖对象。这种情况可以通过 <code>Spiral\Core\FactoryInterface</code> 来实现：</p>
<pre><code class=language-php>public function makeClass(FactoryInterface $factory)
{
    return $factory-&gt;make(MyClass::class, [
        'parameter' =&gt; 'value'
        // 除了明确指定的参数以外，其它依赖会自动解析
    ]);
}
</code></pre>
<h2 id=解析接口>解析接口</h2>
<p>除了明确指明依赖项的类型以外，有时候在调用一个方法时并不确定它的参数类型（比如在抽象类中调用具体实现类的方法），这种情况下，可以借助 <code>Spiral\Core\ResolverInterface</code> 来实现：</p>
<pre><code class=language-php>abstract class Handler
{
    /** @var ResolverInterface */
    protected $resolver;

    public function __construct(ResolverInterface $resolver)
    {
        $this-&gt;resolver = $resolver;
    }

    public function run(array $params)
    {
        $method = new \ReflectionMethod($this, 'do'); // 要调用的包含自动注入参数的方法
        $method-&gt;setAccessible(true);

        return $method-&gt;invokeArgs(
            $this,
            $this-&gt;resolver-&gt;resolveArguments($method, $params) // 解析所需的参数自动注入
        );
    }
}
</code></pre>
<p>现在 <code>do</code> 方法就可以实现自动依赖注入了：</p>
<pre><code class=language-php>class MyHandler extends Handler
{
    public function do(SomeClass $some)
    {
        // ...
    }
}
</code></pre>
<h2 id=自动装配>自动装配</h2>
<p>Spiral 框架通过提供丰富的自动装配功能，尽量对业务层代码隐藏容器的实现和配置。虽然自动装配的规则非常简单，但掌握他们还是很重要的，这样才能避免框架使用中出现一些异常行为。</p>
<h3 id=依赖项自动解析>依赖项自动解析</h3>
<p>框架容器能够自动提供具体类的实例来解决构造函数及其它方法函数的依赖关系。例如：</p>
<pre><code class=language-php>class MyController
{
    public function __construct(OtherClass $class, SomeInterface $some)
    {
    }
}
</code></pre>
<p>在上面的示例中，容器将尝试自动构造 <code>OtherClass</code> 的实例来解决对它的依赖，但是对于 <code>SomeInterface</code> 接口，容器并不清楚要用它的哪一个具体实现来解决依赖，因此需要明确建立接口与要使用的具体实现类之间的绑定关系：</p>
<pre><code class=language-php>$container-&gt;bind(SomeInterface::class, SomeClass::class);
</code></pre>
<p>需要特别注意的是，容器会尝试解决构造函数的 <em>所有</em> 依赖（除非你明确提供了某个依赖的具体值）。这意味着所有的类构造函数依赖参数都必须是可用的，否则的话，该参数需要声明为可选参数：</p>
<pre><code class=language-php>// 如果在实例化该类时没有明确传入 $value 参数，会报错
__construct(OtherClass $class, $value)

// 如果在实例化该类时没有传入 `$value` 参数，会使用 `null`
__construct(OtherClass $class, $value = null)

// 如果 SomeInterface 没有绑定到具体实现，且构造时没有传入，会报错
__construct(OtherClass $class, SomeInterface $some)

// 如果构造时没有提供 SomeInterface 的具体实现，会自动使用 `null`
__construct(OtherClass $class, SomeInterface $some = null)
</code></pre>
<h3 id=上下文自动装配>上下文自动装配</h3>
<p>除了常规的方法注入以外，容器还能自动解析注入依赖上下文。具体来说，这个能力使容器能够在一个方法函数中自动识别同一个类的两个不同实现：</p>
<pre><code class=language-php>protected function index(Database $primary, Database $secondary)
{
    dump($primary);
    dump($secondary);
}
</code></pre>
<blockquote>
<p>上面例子中的 <code>primary</code> 和 <code>secondary</code> 是数据库连接的名称，参见
<a href=/docs/database/configuration/>数据库配置</a>。</p>
</blockquote>
<p>如果要实现自己的依赖注入器，需要实现 <code>Spiral\Core\Container\InjectorInterface</code>：</p>
<p>下面简单通过示例说明如何创建一个依赖注入器：</p>
<pre><code class=language-php>class Injector implements InjectorInterface
{
    public function createInjection(\ReflectionClass $class, string $context = null)
    {
        return new MyClass($context);
    }
}
</code></pre>
<p>上面创建的依赖注入器里用到的 <code>MyClass</code> 类如下：</p>
<pre><code class=language-php>class MyClass
{
    public function __construct(string $name)
    {
        //...
    }
}
</code></pre>
<p>构造完成后，要在容器中注册注入器：</p>
<pre><code class=language-php>$container-&gt;bindInjector(MyClass::class, Injector::class);
</code></pre>
<p>现在我们就可以像下面这样使用 <code>MyClass</code> 作为方法参数了：</p>
<pre><code class=language-php>public function method(MyClass $john, MyClass $bob)
{
    dump($john);
    dump($bob);
}
</code></pre>
<p>在使用 <code>Spiral\Core\FactoryInterface</code> 时，可以始终绕过上下文注入：</p>
<pre><code class=language-php>dump($factory-&gt;make(MyClass::class, ['name' =&gt; 'abc']));
</code></pre>
<h2 id=使用单例模式>使用单例模式</h2>
<p>许多内部应用服务以单例对象的形式驻留在内存中。这些对象不是通过实现静态方法 <code>getInstance</code>, 而是被配置为在多个请求时间保留和复用。</p>
<p>把服务或者控制器声明为单例是获得少许性能改善的捷径，但是在使用时必须要注意一些规则，以避免内存和状态泄露。</p>
<h3 id=定义单例>定义单例</h3>
<p>Spiral 框架提供了很多方法可以把类对象声明为单例。首先，可以通过创建引导程序，在其中把类和它自己的类名绑定：</p>
<pre><code class=language-php>class ServiceBootloader extends Bootloader
{
    protected const SIGNLETONS = [
        Service::class =&gt; Service::class
    ];
}
</code></pre>
<p>如此一来，在注入依赖时，你通过依赖容器请求这个类时得到的始终都是同一个实例。</p>
<p>另一种方法不需要引导程序，可以在类的代码中中进行定义，这种情况需要实现 <code>Spiral\Core\Container\SingletonInterface</code> 接口。实现这个接口等于告诉容器”这个类只实例化一次“：</p>
<pre><code class=language-php>use piral\Core\Container\SingletonInterface;

class Service implements SingletonInterface
{
    // ...
}
</code></pre>
<blockquote>
<p>单例接口可以由服务类、控制器类、中间件类等实现。但不要在数据仓库和关系映射中实现，因为后者的状态由 ORM 进行管理。</p>
</blockquote>
<h3 id=使用限制>使用限制</h3>
<p>把服务保留在内容中实现在多个请求之间复用，可以避免一次又一次地执行复杂的初始化和计算过程。但必须时刻铭记这样的服务必须以 <strong>无状态</strong> 的方式设计，并且不包含任何用户数据。</p>
<p>以下是严格禁止的：</p>
<ul>
<li>在单例中保留用户信息</li>
<li>保留对 PSR-7 请求对象的引用（使用 <code>InputManager</code> 代替）</li>
<li>保留对 session 的引用（使用 <code>SessionScope</code> 代替）</li>
<li>保留 RBAC 身份（使用 <code>GuardScope</code> 代替）</li>
</ul>
<h3 id=预热>预热</h3>
<p>多个用户请求之间不会发生改变的数据可以保留在服务中。比如，应用程序依赖于庞大的 XML 作为配置源。</p>
<pre><code class=language-php>class Service implements SingletonService
{
    private $configCache;


    public function getConfig(): array
    {
        if ($this-&gt;configCache !== null) {
            return $this-&gt;configCache;
        }

        $this-&gt;configCache = $this-&gt;readConfig(); // 复杂的计算或者磁盘读取

        return $this-&gt;configCache;
    }
}
</code></pre>
<p>通过上面代码演示的实现方式，就可以只做一次复杂的计算并保留在内存中，在之后的用户请求中就只从内存中获取这些信息。</p>
</div>
<div class=article-widget>
<div class=post-nav>
<div class=post-nav-item>
<div class=meta-nav>上一页</div>
<a href=/docs/framework/kernel/ rel=next>框架 - 内核与环境</a>
</div>
<div class=post-nav-item>
<div class=meta-nav>下一页</div>
<a href=/docs/framework/application-server/ rel=prev>框架 - 应用服务器</a>
</div>
</div>
</div>
</div>
<div class=body-footer>
<p>最近更新于 2020-04-13</p>
<p class=edit-page>
<a href=https://github.com/krwu/spiraldocs/edit/feat/chinese/zh_CN/framework/container.md>
<i class="fas fa-pen pr-2"></i>编辑本页
</a>
</p>
</div>
</article>
<footer class=site-footer>
<p class=powered-by><span class=copyright>© 2019 - 2021 <a href=https://studyspiral.cn/>StudySpiral</a> All rights reserved.</span>
</p>
<p class=powered-by>
<a href=http://www.beian.miit.gov.cn/ target=_blank rel="noopener noreferer">粤ICP备14011364号</a>
<a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=44030402001866" target=_blank rel="noopener noreferer"><img style=display:inline-block src=/img/gaba.png>粤公网安备 44030402001866号</a>
</p>
<p id=webify class=powered-by style=display:none>[<a href=https://webify.cloudbase.net/ target=_blank rel=noopener>CloudBase Webify</a>] 提供网站托管服务</p>
<script async defer>window.addEventListener('DOMContentLoaded',()=>{const{host:a}=window.location;a.toLocaleLowerCase()==='docs.studyspiral.cn'&&(document.getElementById('webify').style.display='block')})</script>
</footer>
</main>
</div>
</div>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/4.1.4/imagesloaded.pkgd.min.js integrity="sha256-lqvxZrPLtfffUl2G/e7szqSvPBILGbwmsGE1MKlOi0Q=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/3.0.6/isotope.pkgd.min.js integrity="sha256-CBrpuqrMhXwcLLUd5tvQ4euBHCdh7wGlDfNz8vbu/iI=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js integrity="sha256-yt2kYMy0w8AbtF89WXb2P1rfjcP/HTHLT7097U8Y5b8=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/highlight.min.js integrity="sha256-eOgo0OtLL4cdq7RdwRUiGKLX9XsIJ7nGhWEKbohmVAQ=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/languages/go.min.js></script>
<script>const code_highlighting=!0</script>
<script>const isSiteThemeDark=!1</script>
<script>const search_config={indexURI:"/index.json",minLength:1,threshold:.3},i18n={no_results:"没有找到结果",placeholder:"搜索...",results:"搜索结果"},content_type={post:"文章",project:"项目",publication:"出版物",talk:"演讲"}</script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.1/anchor.min.js integrity="sha256-pB/deHc9CGfFpJRjC43imB29Rse8tak+5eXqntO94ck=" crossorigin=anonymous></script>
<script>anchors.add()</script>
<script id=search-hit-fuse-template type=text/x-template>
      <div class="search-hit" id="summary-{{key}}">
      <div class="search-hit-content">
        <div class="search-hit-name">
          <a href="{{relpermalink}}">{{title}}</a>
          <div class="article-metadata search-hit-type">{{type}}</div>
          <p class="search-hit-description">{{snippet}}</p>
        </div>
      </div>
      </div>
    </script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.2.1/fuse.min.js integrity="sha256-VzgmKYmhsGNNN4Ph1kMW+BjoYJM2jV5i4IlFoeZA9XI=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/jquery.mark.min.js integrity="sha256-4HLtjeVgH0eIB3aZ9mLYF6E8oU5chNdjU6p6rrXpl9U=" crossorigin=anonymous></script>
<script src=/js/academic.min.6f1005d1a84220e2f466ff3d8e712f31.js></script>
<div id=modal class="modal fade" role=dialog>
<div class=modal-dialog>
<div class=modal-content>
<div class=modal-header>
<h5 class=modal-title>引用</h5>
<button type=button class=close data-dismiss=modal aria-label=Close>
<span aria-hidden=true>&#215;</span>
</button>
</div>
<div class=modal-body>
<pre><code class="tex hljs"></code></pre>
</div>
<div class=modal-footer>
<a class="btn btn-outline-primary my-1 js-copy-cite" href=# target=_blank>
<i class="fas fa-copy"></i> 复制
</a>
<a class="btn btn-outline-primary my-1 js-download-cite" href=# target=_blank>
<i class="fas fa-download"></i> 下载
</a>
<div id=modal-error></div>
</div>
</div>
</div>
</div>
</body>
</html>